/*!
\copyright  Copyright (c) 2023-2025 Qualcomm Technologies International, Ltd.\n
            All Rights Reserved.\n
            Qualcomm Technologies International, Ltd. Confidential and Proprietary.
\file       usb_audio_class_20.c
\ingroup    usb_audio
\brief      USB Audio Class 2.0 Driver
*/

#include <message.h>
#include <panic.h>
#include <sink.h>
#include <source.h>
#include <string.h>
#include <stream.h>
#include <usb.h>
#include <print.h>
#include <vmal.h>
#include <stdlib.h>
#include "logging.h"

#include <usb_hub.h>

#include "usb_audio_class_defines.h"
#include "usb_device_utils.h"
#include "usb_audio_class_20.h"
#include "usb_audio_class_20_descriptors.h"
#include "usb_audio_source_audio_if/usb_audio_source_audio_if.h"
#include "usb_audio_source_voice_if/usb_audio_source_voice_if.h"

/*! USB audio device class state. */
typedef struct uac_data_t
{
    const usb_audio_config_params_t *config;
    usb_audio_streaming_info_t      *streaming_info;

    uac2_cs_header_desc_t   *cs_header_desc;
    Source                   control_source;
    UsbInterface             control_interface;
    uint8                    num_interfaces;

    uint8 usb_iad_descriptor[USB_DT_INTERFACE_ASSOCIATION_SIZE];

    uac_event_handler_t      evt_handler;
    struct uac_data_t *      next;
} uac_data_t;

/*!
    Audio Gain Limits - We support analogue gain from -45dB to 0dB.
    Digital gain goes up to +21.5 but distorts audio so don't report it.
    Firmware settings for analogue gain are:
    0   1     2   3     4   5     6   7     8   9   10   11 12 12 14 15
    Which correspond to gains (in dB) of:
    -45 -41.5 -39 -35.5 -33 -29.5 -27 -23.5 -21 -18 -15 -12 -9 -6 -3 0
    We report resolution of 3dB so we're always within .5dB of the truth:
    -45 -42   -39 -36   -33 -30   -27 -24   -21 -18 -15 -12 -9 -6 -3 0
*/
#define UAC_VOLUME_MAX                0
#define UAC_VOLUME_MIN              -45
#define UAC_VOLUME_RESOLUTION         3
#define UAC_VOLUME_DEFAULT          -45

TaskData uac2_control_task;
TaskData uac2_streaming_task;

static uac_data_t *audio_class_info = NULL;

/****************************************************************************
    To create an instance of audio class
*/
static uac_data_t * usbAudioClass20_GetNewInstance(void)
{
    uac_data_t *uac_info = (uac_data_t *)PanicUnlessMalloc(sizeof(uac_data_t));
    memset(uac_info, 0, sizeof(uac_data_t));
    
    uac_info->next = audio_class_info;
    audio_class_info = uac_info;
    return uac_info;
}

/****************************************************************************
    To get audio class context from control source
*/
static uac_data_t * usbAudioClass20_GetInfoFromControlSource(Source source)
{
    uac_data_t *uac_info = audio_class_info;
    while(uac_info)
    {
        if(uac_info->control_source == source)
        {
            return uac_info;
        }
        uac_info = uac_info->next;
    }
    return NULL;
}

/****************************************************************************
    To get audio stream context from streaming source, also returns interface index
*/
static uac_data_t * usbAudioClass20_GetInfoFromStreamingSource(Source source, uint8 *intf_index_ptr)
{
    uac_data_t *uac_info = audio_class_info;
    while (uac_info)
    {
        for (uint8 i=0; i < uac_info->num_interfaces; i++)
        {
            if (uac_info->streaming_info[i].source == source)
            {
                *intf_index_ptr = i;
                return uac_info;
            }
        }
        uac_info = uac_info->next;
    }
    return NULL;
}

/****************************************************************************
    To get audio stream context from Feature Unit ID
*/

/*
 * Parse AudioControl descriptors and find feature unit id
 */
static bool usbAudioClass20_GetFeatureUnitID(const uac_control_config_t *control_desc,
                                             uint8 *feature_unit_id_ptr)
{
    const uint8 *ptr = control_desc->descriptor;
    const uint8 *end = ptr + control_desc->size_descriptor;

    while (ptr < end)
    {
        const uac_feature_unit_descriptor_t *fu_desc =
                (const uac_feature_unit_descriptor_t *)ptr;

        if (fu_desc->bDescriptorType == UAC_CS_DESC_INTERFACE &&
            fu_desc->bDescriptorSubtype == UAC_AC_DESC_FEATURE_UNIT)
        {
            *feature_unit_id_ptr = fu_desc->bUnitID;
            return TRUE;
        }
        ptr += fu_desc->bLength;
    }
    return FALSE;
}

/*
 * Parse AudioControl descriptors and find clock source unit id
 */
static bool usbAudioClass20_CheckClockSourceDesc(const uac_control_config_t *control_desc,
        uint8 cs_unit_id)
{
    bool seen_cs_desc = FALSE;
    bool seen_input_term = FALSE;
    bool seen_output_term = FALSE;

    const uint8 *ptr = control_desc->descriptor;
    const uint8 *end = ptr + control_desc->size_descriptor;

    while (ptr < end)
    {
        const uac_generic_descriptor_t *desc = (const uac_generic_descriptor_t *) ptr;

        if (desc->bDescriptorType == UAC_CS_DESC_INTERFACE)
        {
            switch (desc->bDescriptorSubtype)
            {
                case UAC_AC_DESC_CLOCK_SOURCE:
                {
                    const uac2_clock_source_desc_t *cs_desc =
                                    (const uac2_clock_source_desc_t *)ptr;
                    if (cs_desc->bClockID == cs_unit_id)
                    {
                        seen_cs_desc = TRUE;
                    }

                    break;
                }
                case UAC_AC_DESC_INPUT_TERMINAL:
                {
                    const uac_input_terminal_descriptor_t *it_desc =
                            (const uac_input_terminal_descriptor_t *)desc;
                    if (it_desc->bCSourceID == cs_unit_id)
                    {
                        seen_input_term = TRUE;
                    }
                    break;
                }
                case UAC_AC_DESC_OUTPUT_TERMINAL:
                {
                    const uac_output_terminal_descriptor_t *ot_desc =
                            (const uac_output_terminal_descriptor_t *)desc;
                    if (ot_desc->bCSourceID == cs_unit_id)
                    {
                        seen_output_term = TRUE;
                    }
                    break;
                }
                default:
                    break;
            }
        }
        ptr += desc->bLength;
    }
    return seen_cs_desc && seen_input_term && seen_output_term;
}

/*
 * Parse AudioStreaming descriptors and return format descriptor
 */
static const uac2_format_descriptor_t  *usbAudioClass20_GetStreamingFormatDescriptor(const uac_streaming_config_t *streaming_config)
{
    const uint8 *ptr = streaming_config->intf_descriptor;
    const uint8 *end = ptr + streaming_config->size_descriptor;

    while (ptr < end)
    {
        const uac2_format_descriptor_t* format =
                (const uac2_format_descriptor_t*) ptr;

        if (format->bDescriptorType == UAC_CS_DESC_INTERFACE &&
            format->bDescriptorSubtype == UAC_AS_DESC_FORMAT_TYPE &&
            format->bFormatType == UAC_FORMAT_TYPE_I)
        {
            return format;
        }
        ptr += format->bLength;
    }

    return NULL;
}

static const uac2_as_general_descriptor_t *usbAudioClass20_GetStreamingGeneralDescriptor(
                                 const uac_streaming_config_t *streaming_config)
{
    const uint8 *ptr = streaming_config->intf_descriptor;
    const uint8 *end = ptr + streaming_config->size_descriptor;

    while (ptr < end)
    {
        const uac2_as_general_descriptor_t* general =
                (const uac2_as_general_descriptor_t*) ptr;

        if (general->bDescriptorType == UAC_CS_DESC_INTERFACE &&
                general->bDescriptorSubtype == UAC_AS_DESC_GENERAL)
        {
            return general;
        }
        ptr += general->bLength;
    }
    return NULL;
}

/****************************************************************************
    To get maximum sampling rate supported by the streaming interface endpoint
*/
static uint32 usbAudioClass20_GetMaxSamplingRate(const uac_clock_source_config_t *source)
{
    uint32 max_rate = 0;

    uint8 freq_index;
    DEBUG_LOG_WARN("uac2: get max freq count %d", source->num_frequencies);
    for (freq_index = 0; freq_index < source->num_frequencies; freq_index++)
    {

        if (max_rate < source->frequencies[freq_index])
        {
            max_rate = source->frequencies[freq_index];
        }
        DEBUG_LOG_WARN("uac2: freq[%d] curr %d max %d",
                freq_index, source->frequencies[freq_index], max_rate);
    }

    return max_rate;
}

/****************************************************************************
    To get maximum packet size need by the streaming interface endpoint
*/
static uint16 usbAudioClass20_GetMaxPacketSize(const uac_streaming_config_t *streaming_config,
        uint32 max_sample_rate)
{
    const uac2_format_descriptor_t *format_desc =
            usbAudioClass20_GetStreamingFormatDescriptor(streaming_config);
    assert(format_desc);

    const uac2_as_general_descriptor_t *as_general_desc =
            usbAudioClass20_GetStreamingGeneralDescriptor(streaming_config);
    assert(as_general_desc);

    uint32 num_samples = max_sample_rate / 8;
    num_samples *= (1 << (streaming_config->endpoint->bInterval-1));

    uint32 max_packet_size = UAC_MAX_PACKET_SIZE(num_samples,
                                                 as_general_desc->bNrChannels,
                                                 format_desc->bSubslotSize);

    // dmitriiv: TODO we don't understand this
    max_packet_size *= 2;

    return max_packet_size;
}

/****************************************************************************
    To write the value of required attribute to sink
*/
static uint16 usbAudioClass20_GetLevel(usb_audio_streaming_info_t *streaming_info,
                                       const usb_audio_volume_config_t *volume_config,
                                       Sink sink, uint8 control, uint8 code, uint8 channel)
{
    uint8 *p_snk;

    if (channel == UAC_CHANNEL_MASTER)
    {
        if (control == UAC_FU_MUTE_CONTROL)
        {
            if (code == UAC2_CUR)
            {
                DEBUG_LOG_INFO("audioGetLevel:MUTE: intf - %d, VAL - %d ",
                          streaming_info->interface,
                          streaming_info->volume_status.mute_status);

                p_snk = SinkMapClaim(sink, 1);
                if (p_snk)
                {
                    p_snk[0] = streaming_info->volume_status.mute_status;
                    return 1;
                }
                else
                {
                    DEBUG_LOG_WARN("audioGetLevel: SinkClaim Failed");
                    return 0;
                }
            }
        }
        else if (control == UAC_FU_VOLUME_CONTROL)
        {
            uint8 value = 0;

            if (code == UAC2_CUR)
            {
                value = (uint8)streaming_info->volume_status.volume_db;

                DEBUG_LOG_INFO("uac2: get VOL: intf - %d, VAL - %d",
                           streaming_info->interface, (int8)value);

                p_snk = SinkMapClaim(sink, 2);
                if (p_snk)
                {
                    p_snk[0] = 0;
                    p_snk[1] = value;
                    return 2;
                }
                else
                {
                    return 0;
                }
            }
            else if (code == UAC2_RANGE)
            {
                DEBUG_LOG_INFO("uac2: get VOL: intf - %d, RANGE - (%d,%d,%d)",
                                           streaming_info->interface,
                                           (int8)volume_config->min_db,
                                           (int8)volume_config->max_db,
                                           (int8)volume_config->res_db);

                p_snk = SinkMapClaim(sink, 8);
                if (p_snk)
                {
                    p_snk[0] = 1; /* wNumSubRanges */
                    p_snk[1] = 0;

                    p_snk[2] = 0; /* wMin */
                    p_snk[3] = (uint8)volume_config->min_db;

                    p_snk[4] = 0; /* wMax */
                    p_snk[5] = (uint8)volume_config->max_db;

                    p_snk[6] = 0; /* wMax */
                    p_snk[7] = (uint8)volume_config->res_db;

                    return 8;
                }
                else
                {
                    return 0;
                }
            }
        }
    }
    /* Unsupported request */
    DEBUG_LOG_WARN("audioGetLevel: Unsupported request: intf - %d, "
                   "Control - %d, Code - %d, Channel - %d",
                   streaming_info->interface, control, code, channel);
    return 0;
}

/****************************************************************************
    To round the gain based on volume configuration.
*/
static int8 usbAudioClass20_RoundVolume(const usb_audio_volume_config_t *volume_config,
                                        int8 volume)
{
    int8 max_db = volume_config->max_db;
    int8 min_db = volume_config->min_db;
    uint8 res_db = volume_config->res_db;

    /* Don't round up to maximum or round down to minimum to avoid
     * max/min prompts when host volume is at 99% / 1%. */

    if (volume >= max_db)
    {
        return max_db;
    }
    else if (volume <= min_db)
    {
        return min_db;
    }
    /* round up volumes in the range (min_db, min_db + res_db] */
    else if (volume <= min_db + res_db)
    {
        return (int8)(min_db + res_db);
    }

    /* this code rounds down, so values [max_db - res_db, max_db) are fine. */
    return (int8)(min_db + (volume - min_db) / res_db * res_db);
}

/****************************************************************************
    Read the level value from the source, store it in the volume control and
    notify Application with the USB_DEVICE_CLASS_MSG_AUDIO_LEVELS_IND message.
*/
static bool usbAudioClass20_SetLevel(usb_audio_streaming_info_t * streaming_info,
                                     const usb_audio_volume_config_t *volume_config,
                                     Source source, uint8 control, uint8 code, uint8 channel)
{
    uint16 data_len = SourceBoundary(source);

    /* Only allow setting current levels */
    if (code == UAC2_CUR &&
            channel == UAC_CHANNEL_MASTER)
    {
        const uint8* p_src = SourceMap(source);

        if (control == UAC_FU_MUTE_CONTROL &&
                data_len == 1)
        {
            streaming_info->volume_status.mute_status = p_src[0] ? 1 : 0;
            DEBUG_LOG_INFO("audioSetLevel: MUTE: intf - %d,  VAL - %d",
                            streaming_info->interface,
                            streaming_info->volume_status.mute_status);
            /* Respond with success */
            return TRUE;
        }
        else if (control == UAC_FU_VOLUME_CONTROL &&
                data_len == 2)
        {
            /* ignore the low byte with 1/256 db fractions as we only support
             * integer values. */
            int8 volume = p_src[1];
            streaming_info->volume_status.volume_db =
                    usbAudioClass20_RoundVolume(volume_config, volume);

            DEBUG_LOG_INFO("audioSetLevel: VOL: intf - %d, VAL - %d",
                                     streaming_info->interface,
                                     streaming_info->volume_status.volume_db);
            /* Respond with success */
            return TRUE;
        }
    }

    /* Unsupported request */
    DEBUG_LOG_WARN("audioSetLevel: Unsupported request: intf - %d, "
                    "Control - %d, Code - %d, Channel - %d, data len %d",
                    streaming_info->interface, control, code, channel, data_len);
    return FALSE;
}

static uint16 usbAudioClass20_GetClockSource(
        usb_audio_streaming_info_t *streaming_info,
        const uac_clock_source_config_t *clock_source_config,
        Sink sink, uint8 control, uint8 code, uint8 channel)
{
    uint16 ret_len = 0;

    if (control == UAC2_CS_SAM_FREQ_CONTROL &&
            channel == 0)
    {
        if (code == UAC2_CUR)
        {
            ret_len = 4;

            uint8 *data = SinkMapClaim(sink, ret_len);
            if (!data)
            {
                DEBUG_LOG_ERROR("uac2: can't claim sink for class response");

                ret_len = 0;
            }
            else
            {
                uint32 value = streaming_info->current_sampling_rate;

                DEBUG_LOG_INFO("uac2: get FREQ: VAL - %u", value);

                data[0] = (uint8)value;
                data[1] = (uint8)(value >> 8);
                data[2] = (uint8)(value >> 16);
                data[3] = (uint8)(value >> 24);
            }
        }
        else if (code == UAC2_RANGE)
        {
            ret_len = clock_source_config->num_frequencies * 4 * 3 + 2;

            uint8 *data = SinkMapClaim(sink, ret_len);
            if (!data)
            {
                DEBUG_LOG_ERROR("uac2: can't claim sink for class response");

                ret_len = 0;
            }
            else
            {
                int pos = 0;
                data[pos++] = clock_source_config->num_frequencies; /* wNumSubRanges */
                data[pos++] = 0;

                uint8 i;
                for (i=0; i < clock_source_config->num_frequencies; i++)
                {
                    uint32 min = clock_source_config->frequencies[i];
                    uint32 max = min;
                    uint32 res = 0;

                    data[pos++] = min; /* wMin */
                    data[pos++] = min >> 8;
                    data[pos++] = min >> 16;
                    data[pos++] = min >> 24;

                    data[pos++] = max; /* wMax */
                    data[pos++] = max >> 8;
                    data[pos++] = max >> 16;
                    data[pos++] = max >> 24;

                    data[pos++] = res; /* wRes */
                    data[pos++] = res >> 8;
                    data[pos++] = res >> 16;
                    data[pos++] = res >> 24;

                    DEBUG_LOG_INFO("uac2: get FREQ: RANGE(%d) - (%u,%u,%u)", i, min, max, res);
                }
            }
        }
    }

    /* UAC 2.0, 5.2.5.1.2 Clock Validity Control
     *
     * The Clock Validity Control is used to indicate if the clock signal
     * that is generated by the Clock Source Entity is valid
     */
    else if (control == UAC2_CS_CLOCK_VALID_CONTROL &&
             channel == 0 &&  // Channel Number field must be set to zero (master Control).
             code == UAC2_CUR) // Control must have only the CUR attribute
    {
        ret_len = 1;

        uint8 *data = SinkMapClaim(sink, ret_len);
        if (!data)
        {
            DEBUG_LOG_ERROR("uac2: can't claim sink for class response");

            ret_len = 0;
        }
        else
        {
            /* The value of a Clock Validity Control CUR attribute
             * must be either TRUE (clock valid) or FALSE (clock invalid). */
            data[0] = 1;
        }
    }

    if (!ret_len)
    {
        DEBUG_LOG_ERROR("uac2: ERROR - GetClockSource - ctrl %d code %d ch %d",
                control, code, channel);
    }

    return ret_len;
}

static bool usbAudioClass20_SetClockSource(
        usb_audio_streaming_info_t *streaming_info,
        const uac_clock_source_config_t *clock_source_config,
        Source source, uint8 control, uint8 code, uint8 channel)
{

    bool result = FALSE;

    if (control == UAC2_CS_SAM_FREQ_CONTROL &&
            channel == 0)
    {
        uint16 data_len = SourceBoundary(source);
        const uint8 *data = SourceMap(source);

        /* Only allow setting current levels */
        if (code == UAC2_CUR && data_len == 4)
        {
            uint32 value = data[0] |
                    (data[1] << 8) |
                    (data[2] << 16) |
                    (data[3] << 24);

            for (uint8 i=0; i < clock_source_config->num_frequencies; i++)
            {
                if (clock_source_config->frequencies[i] == value)
                {
                    DEBUG_LOG_INFO("uac2: itf[%d] sampling frequency %u requested",
                            streaming_info->interface, value);
                    streaming_info->current_sampling_rate = value;
                    /* Respond with success */
                    result = TRUE;
                }
            }
        }
        else
        {
            /* Unsupported request */
            DEBUG_LOG_WARN("uac2: unsupported SET FREQ: Code - %d Len - %u", code, data_len);
        }
    }

    return result;
}

/****************************************************************************
    To handle audio control request
*/
static void usbAudioClass20_HandleControlClassRequest(uac_data_t * uac_info)
{
    const usb_audio_volume_config_t *volume_config = &uac_info->config->volume_config;
    usb_audio_streaming_info_t *streaming_info;
    uint16 packet_size;
    Source req = uac_info->control_source;
    Sink sink = StreamSinkFromSource(req);
    uint8 interface, unit_id, channel, code, control;

    /* Check for outstanding Class requests */
    while ((packet_size = SourceBoundary(req)) != 0)
    {
        /*
            Build the response. It must contain the original request,
            so copy from the source header.
        */
        UsbResponse usbresp;
        memcpy(&usbresp.original_request, SourceMapHeader(req), sizeof(UsbRequest));

        /* Reject by default */
        usbresp.data_length = 0;
        usbresp.success = FALSE;

        /* Get audio specific request info */
        channel   = REQ_CN(usbresp.original_request);
        control   = REQ_CS(usbresp.original_request);
        code      = REQ_CODE(usbresp.original_request);
        unit_id   = REQ_UNIT(usbresp.original_request);
        interface = REQ_INTERFACE(usbresp.original_request);

        if (interface == uac_info->control_interface)
        {
            for (uint8 index=0; index < uac_info->num_interfaces; index++)
            {
                streaming_info = &uac_info->streaming_info[index];

                if (streaming_info->clock_source == unit_id)
                {
                    /* Directed to a clock source ID - frequence control */
                    const usb_audio_interface_config_t *intf_config =
                            &uac_info->config->intf_list->intf[index];

                    const uac_clock_source_config_t *clock_source_config = NULL;

                    for (uint8 cs_index = 0;
                            cs_index < intf_config->clock_sources_count;
                            cs_index++)
                    {
                        if (intf_config->clock_sources[cs_index].clock_source ==
                                streaming_info->clock_source)
                        {
                            clock_source_config = &intf_config->clock_sources[cs_index];
                        }
                    }

                    assert(clock_source_config);

                    if (REQ_IS_GET(usbresp.original_request))
                    {
                        DEBUG_LOG_INFO("AudioControlClassRequest: GET clock source");

                        usbresp.data_length = usbAudioClass20_GetClockSource(
                                streaming_info, clock_source_config,
                                sink, control, code, channel);
                        usbresp.success = (usbresp.data_length != 0);
                    }
                    else
                    {
                        DEBUG_LOG_INFO("AudioControlClassRequest: SET clock source");
                        usbresp.success = usbAudioClass20_SetClockSource(
                                streaming_info, clock_source_config,
                                req, control, code, channel);
                        if (usbresp.success)
                        {
                            /* notify change in sample rate */
                            uac_info->evt_handler(uac_info, index,
                                                  USB_AUDIO_CLASS_MSG_SAMPLE_RATE);
                        }
                    }
                }
                else if (streaming_info->feature_unit_id == unit_id)
                {
                    /* Directed to a feature unit - volume/mute control */

                    if (REQ_IS_GET(usbresp.original_request))
                    {
                        /* Return value to USB host */
                        DEBUG_LOG_INFO("AudioControlClassRequest: GET");
                        usbresp.data_length = usbAudioClass20_GetLevel(streaming_info,
                                                                       volume_config,
                                                                       sink, control, code, channel);
                        usbresp.success = (usbresp.data_length != 0);
                    }
                    else
                    {
                        /* Update value sent by USB host */
                        DEBUG_LOG_INFO("AudioControlClassRequest: SET");
                        usbresp.success = usbAudioClass20_SetLevel(streaming_info,
                                                                   volume_config,
                                                                   req, control, code, channel);
                        if(usbresp.success)
                        {
                            /* Level has changed, notify application */
                            uac_info->evt_handler(uac_info, index,
                                                  USB_AUDIO_CLASS_MSG_LEVELS);
                        }
                    }
                }
            }
        }
        else
        {
            usbresp.success = FALSE;

            DEBUG_LOG_ERROR("AudioControlClassRequest: Unsupported request: uac_info: %X, "
                            "Intf: %d, Unit_id: %d, Control: %d, Code: %d, Channel: %d",
                            uac_info, interface, unit_id, control, code, channel);
        }

        if(!usbresp.success)
        {
            DEBUG_LOG_WARN("AudioControlClassRequest: Failed \n");
        }

        /* Send response */
        if (usbresp.data_length)
        {
            (void)SinkFlushHeader(sink, usbresp.data_length, (void *)&usbresp, sizeof(UsbResponse));
        }
        else
        {
            /* Sink packets can never be zero-length, so flush a dummy byte */
            (void)SinkClaim(sink, 1);
            (void)SinkFlushHeader(sink, 1, (void *)&usbresp, sizeof(UsbResponse));
        }

        /* Discard the original request */
        SourceDrop(req, packet_size);
    }
}

/****************************************************************************
    To handle audio streaming request
*/
static void usbAudioClass20_HandleStreamingClassRequest(uac_data_t *uac_info, uint8 intf_index)
{
    usb_audio_streaming_info_t *streaming_info = &uac_info->streaming_info[intf_index];

    Source req = streaming_info->source;
    Sink sink = StreamSinkFromSource(req);
    uint16 packet_size;

    /* Check for outstanding Class requests */
    while ((packet_size = SourceBoundary(req)) != 0)
    {
        /*
            Build the response. It must contain the original request,
            so copy from the source header.
        */
        UsbResponse usbresp;
        memcpy(&usbresp.original_request, SourceMapHeader(req), sizeof(UsbRequest));

        /* Set the response fields to default values to make the code below simpler */
        usbresp.success = FALSE;
        usbresp.data_length = 0;

        DEBUG_LOG_INFO("UAC2_HandleStreamingClassRequest: intf_index %d, interface %d, desc_index %d",
                         intf_index, streaming_info->interface, streaming_info->streaming_desc_index);


        /* UAC2 does not have any requests here! */

        /* Send response */
        if (usbresp.data_length)
        {
            (void)SinkFlushHeader(sink, usbresp.data_length, (void *)&usbresp, sizeof(UsbResponse));
        }
        else
        {
            /* Sink packets can never be zero-length, so flush a dummy byte */
            (void)SinkClaim(sink, 1);
            (void)SinkFlushHeader(sink, 1, (void *)&usbresp, sizeof(UsbResponse));
        }

        /* Discard the original request */
        SourceDrop(req, packet_size);
    }

}


/****************************************************************************
    To handle audio cotrol request from all device indexes
*/
static void usbAudioClass20_ControlHandle(Task task, MessageId id, Message message)
{
    Source class_source = 0;
    UNUSED(task);

    if (id == MESSAGE_MORE_DATA)
    {
        class_source = ((MessageMoreData *)message)->source;
        uac_data_t *uac_info = usbAudioClass20_GetInfoFromControlSource(class_source);
        if (uac_info != NULL)
        {
            usbAudioClass20_HandleControlClassRequest(uac_info);
            return;
        }
    }

    DEBUG_LOG_WARN("UAC2: unhandled control message id 0x%d source 0x%x", id, class_source);
}

/****************************************************************************
    To handle audio streaming request
*/
static void usbAudioClass20_StreamingHandler(Task task, MessageId id, Message message)
{
    Source class_source = 0;

    UNUSED(task);
    if (id == MESSAGE_MORE_DATA)
    {
        class_source = ((MessageMoreData *)message)->source;
        uint8 intf_index;
        uac_data_t *uac_info = usbAudioClass20_GetInfoFromStreamingSource(class_source, &intf_index);
        if (uac_info != NULL)
        {
            usbAudioClass20_HandleStreamingClassRequest(uac_info, intf_index);
            return;
        }
    }
    else if (id == MESSAGE_MORE_SPACE)
    {
    }

    DEBUG_LOG_WARN("UAC2: unhandled stream message id 0x%d source 0x%x", id, class_source);
}

static const uint8 *usbAudioClass20_FindEndpointClassDescriptor(const uint8 *intf_desc,
        uint16 intf_desc_size)
{
    const uint8 *ptr = intf_desc;
    const uint8 *end = ptr + intf_desc_size;

    while (ptr < end)
    {
        const uac_generic_descriptor_t* desc =
                (const uac_generic_descriptor_t *) ptr;

        if (desc->bDescriptorType == UAC_CS_DESC_ENDPOINT)
        {
            return ptr;
        }
        ptr += desc->bLength;
    }
    return NULL;
}

/****************************************************************************
 * To add streaming interface for enumeration
 *
 * The UsbAddInterface call creates bAlternateSetting=0 without endpoints.
 * UsbAddEndPoints call creates another alternate interface bAlternateSetting=1
 * which contains all the endpoints and class interface descriptors.
 * Any additional alternate interfaces can be added with UsbAddAltInterface and get
 * consecutive bAlternateSetting indices. Alternate interfaces are configured using
 * UsbAddEndPoints and UsbAddDescriptor calls following UsbAddAltInterface calls
 * Refer UsbAddAltInterface().
 * So streaming interface descriptor index can be derived from bAlternateSetting as
 * streaming_desc_index = (bAlternateSetting - 1) where bAlternateSetting != 0.
 * bAlternateSetting = 0 indicate no bandwidth interface.
*/
static void usbAudioClass20_EnumerateStreaming(usb_device_index_t usb_device_index,
                                               usb_audio_streaming_info_t *streaming_info,
                                               const usb_audio_interface_config_t *intf_config)
{
    uint8 ep_address;
    EndPointInfo ep_info;
    const uac_streaming_config_t *streaming_config;

    if (!usbAudioClass20_GetFeatureUnitID(intf_config->control_desc,
                                          &streaming_info->feature_unit_id))
    {
        DEBUG_LOG_ERROR("EnumerateAudioStreaming: Feature unit_id not found");
        Panic();
    }

    /* USB endpoint information */
    ep_address = UsbDevice_AllocateEndpointAddress(usb_device_index,
                                                   intf_config->streaming_config[0].endpoint->is_to_host);
    memset(&ep_info, 0, sizeof(ep_info));

    ep_info.bEndpointAddress = ep_address;
    ep_info.bmAttributes = end_point_attr_iso_sync;
    ep_info.extended = NULL;
    ep_info.extended_length = 0;

    PanicZero(intf_config->alt_settings_count);

    for(uint8 index = 0; index<intf_config->alt_settings_count ; index++)
    {
        streaming_config = &intf_config->streaming_config[index];

        const uint8* intf_desc = streaming_config->intf_descriptor;
        uint16 intf_desc_size = streaming_config->size_descriptor;

        const uint8* ep_desc = NULL;
        uint16 ep_desc_size = 0;

        /** By convention Audio Endpoint Class descriptors are at the tail
         * of Audio Interface Class descriptors, find them and update
         * pointers and sizes. */
        ep_desc = usbAudioClass20_FindEndpointClassDescriptor(intf_desc, intf_desc_size);
        if (ep_desc)
        {
            uint16 new_intf_desc_size = (uint16)(ep_desc - intf_desc);
            assert(new_intf_desc_size < intf_desc_size);

            ep_desc_size = intf_desc_size - new_intf_desc_size;
            intf_desc_size = new_intf_desc_size;
        }

        if(index == 0)
        {
            UsbCodes codes;

            codes.bInterfaceClass = USB_CLASS_AUDIO;
            codes.bInterfaceSubClass = UAC_IF_SUBCLASS_AUDIOSTREAMING;
            codes.bInterfaceProtocol = UAC_FN_PROTOCOL_VERSION_02_00;
            codes.iInterface = I_INTERFACE_INDEX;

            /* Add the speaker Audio Streaming Interface */
            streaming_info->interface = UsbAddInterface(&codes, UAC_CS_DESC_INTERFACE,
                                                        intf_desc, intf_desc_size);

            DEBUG_LOG_INFO("UAC2: streaming enum:usb_audio_device_type_t:%d interface %d",
                    intf_config->type, streaming_info->interface);

            if (streaming_info->interface == usb_interface_error)
            {
                DEBUG_LOG_ERROR("EnumerateAudioStreaming: UsbAddInterface Failed");
                Panic();
            }
        }
        else
        {
            UsbAltInterface alt_setting = UsbAddAltInterface(streaming_info->interface,
                                                             intf_desc, intf_desc_size);
            if (alt_setting == usb_alt_interface_error)
            {
                DEBUG_LOG_ERROR("EnumerateAudioStreaming: UsbAddAltInterface Failed");
                Panic();
            }
        }

        ep_info.bInterval = streaming_config->endpoint->bInterval;
        ep_info.wMaxPacketSize = streaming_config->endpoint->wMaxPacketSize ?
                      streaming_config->endpoint->wMaxPacketSize :
                      usbAudioClass20_GetMaxPacketSize(streaming_config,
                                                       streaming_info->max_sample_rate);

        /* Add the speaker endpoint */
        if (!UsbAddEndPoints(streaming_info->interface, 1, &ep_info))
        {
            DEBUG_LOG_ERROR("EnumerateAudioStreaming: UsbAddEndPoints Failed");
            Panic();
        }

        if (ep_desc)
        {
            uint16 type = ((ep_info.bEndpointAddress << 8) | UAC_CS_DESC_ENDPOINT);
            if (!UsbAddDescriptor(streaming_info->interface, type,
                                  ep_desc, ep_desc_size))
            {
                DEBUG_LOG_ERROR("EnumerateAudioStreaming::UsbAddDescriptor ERROR");
                Panic();
            }
        }
    }

    /* This is platform-dependent data and shall be moved outside UAC2 class */
    enum
    {
        USB20S_CTRL_IRQ_APPS = 0,
        USB20S_CTRL_IRQ_SENSOR = 1,
        USB20S_CTRL_IRQ_AUDIO = 2,
    };

    /** Assign streaming interface to the Audio subsystem */
    UsbInterfaceExternal(streaming_info->interface, USB20S_CTRL_IRQ_AUDIO);

    streaming_info->ep_address = ep_address;
}

/****************************************************************************
    To Initialize audio control cs header descriptor based on streaming interface count
    and size of all Unit Descriptors and Terminal Descriptors
*/
static uac2_cs_header_desc_t * usbAudioClass20_GetHeaderDesc(uint16 unit_terminal_size)
{
    uint16 total_size = UAC2_AC_IF_HEADER_DESC_SIZE + unit_terminal_size;
    uac2_cs_header_desc_t * cs_header = (uac2_cs_header_desc_t *)
                                 PanicUnlessMalloc(UAC2_AC_IF_HEADER_DESC_SIZE);

    cs_header->bLength = UAC2_AC_IF_HEADER_DESC_SIZE;
    cs_header->bDescriptorType = UAC_CS_DESC_INTERFACE;
    cs_header->bDescriptorSubType = UAC_AC_DESC_HEADER;
    cs_header->bcdADC_lo = UAC_BCD_ADC_2_0 & 0xFF;
    cs_header->bcdADC_hi = (UAC_BCD_ADC_2_0 >> 8) & 0xFF;
    cs_header->bCategory = UAC2_HEADSET;
    cs_header->wTotalLength_lo = total_size & 0xFF;
    cs_header->wTotalLength_hi = (total_size >> 8) & 0xFF;
    cs_header->bmControls  = 0;

    return cs_header;
}

/****************************************************************************
    To add control interface for enumeration
*/
static void usbAudioClass20_EnumerateControl(uac_data_t * uac_info)
{
    uint16 unit_terminal_size = 0;
    const usb_audio_interface_config_list_t *intf_list = uac_info->config->intf_list;

    for (uint8 i=0; i < uac_info->num_interfaces; i++)
    {
        unit_terminal_size += intf_list->intf[i].control_desc->size_descriptor;
    }

    uac_info->cs_header_desc = usbAudioClass20_GetHeaderDesc(unit_terminal_size);

    UsbCodes codes;

    codes.bInterfaceClass = USB_CLASS_AUDIO;
    codes.bInterfaceSubClass = UAC_IF_SUBCLASS_AUDIOCONTROL;
    codes.bInterfaceProtocol = UAC_FN_PROTOCOL_VERSION_02_00;
    codes.iInterface = 0;

    /* Add an Audio Control Interface */
    uac_info->control_interface = UsbAddInterface(&codes,
                                              UAC_CS_DESC_INTERFACE,
                                              (uint8 *)uac_info->cs_header_desc,
                                              UAC2_AC_IF_HEADER_DESC_SIZE);
    DEBUG_LOG_INFO("UAC2: control interface %d", uac_info->control_interface);

    if (uac_info->control_interface == usb_interface_error)
    {
        DEBUG_LOG_ERROR("EnumerateAudioControl: usb_interface_error");
        Panic();
    }

    for (uint8 i=0; i < uac_info->num_interfaces; i++)
    {
        const usb_audio_interface_config_t *intf_config = &intf_list->intf[i];

        /* streaming interfaces will be added right after the control interface
         * and get consecutive numbers */

        if (!UsbAddDescriptor(uac_info->control_interface, UAC_CS_DESC_INTERFACE,
                              intf_config->control_desc->descriptor,
                              intf_config->control_desc->size_descriptor))
        {
            DEBUG_LOG_ERROR("UsbAudio_Control::UsbAddDescriptor ERROR");
            Panic();
        }
    }
}

/****************************************************************************
    To manage stream connection of audio class
*/
static void usbAudioClass20_ManageStreamConnection(uac_data_t * uac_info)
{
    Sink sink;
    usb_audio_streaming_info_t *streaming_info;

    uac2_control_task.handler = usbAudioClass20_ControlHandle;
    uac2_streaming_task.handler = usbAudioClass20_StreamingHandler;

    sink = StreamUsbClassSink(uac_info->control_interface);
    uac_info->control_source = StreamSourceFromSink(sink);
    MessageStreamTaskFromSink(sink, &uac2_control_task);

    for (uint8 i=0; i < uac_info->num_interfaces; i++)
    {
        streaming_info = &uac_info->streaming_info[i];
        sink = StreamUsbClassSink(streaming_info->interface);
        streaming_info->source = StreamSourceFromSink(sink);
        MessageStreamTaskFromSink(sink, &uac2_streaming_task);
    }

    StreamConfigure(VM_STREAM_USB_ALT_IF_MSG_ENABLED, 1);
}

static void usbAudioClass20_Set_Defaults(uac_data_t *uac_info)
{
    usb_audio_streaming_info_t *streaming_info = uac_info->streaming_info;
    const usb_audio_config_params_t *config = uac_info->config;

    for (uint8 i=0; i < uac_info->num_interfaces; i++)
    {
        const usb_audio_interface_config_t *intf_config = &config->intf_list->intf[i];
        //uac_format_descriptor_t *format_descriptor;

        streaming_info[i].alt_setting = USB_ALT_SETTING_ZERO;

        /* Using streaming_desc_index 0 as default */
        const uac2_format_descriptor_t *format_desc =
                usbAudioClass20_GetStreamingFormatDescriptor(intf_config->streaming_config);
        assert(format_desc);
        streaming_info[i].streaming_desc_index = 0;
        streaming_info[i].frame_size = format_desc->bSubslotSize;

        const uac2_as_general_descriptor_t *as_general_desc =
                usbAudioClass20_GetStreamingGeneralDescriptor(intf_config->streaming_config);
        assert(as_general_desc);
        streaming_info[i].channels = as_general_desc->bNrChannels;

        /* set speaker default volume */
        streaming_info[i].volume_status.mute_status = 0;
        streaming_info[i].volume_status.volume_db = config->volume_config.target_db;

        if (intf_config->clock_sources_count != 1)
        {
            DEBUG_LOG_ERROR("uac2: interface %d clock sources count %d but only 1 supported",
                    i, intf_config->clock_sources_count);
            Panic();
        }

        streaming_info[i].clock_source = intf_config->clock_sources[0].clock_source;

        /* Make sure that the clock source mentioned in IT/OT descriptors and
         * has CS descriptor */
        if (!usbAudioClass20_CheckClockSourceDesc(intf_config->control_desc,
                                                  streaming_info[i].clock_source))
        {
            DEBUG_LOG_ERROR("uac2: interface %d does not have CS descriptor with unit_id %d",
                    i, streaming_info[i].clock_source);
            Panic();
        }


        DEBUG_LOG_WARN("uac2: intf %d to_host:%d clock_source %d",
                i, intf_config->streaming_config->endpoint->is_to_host,
                streaming_info[i].clock_source);

        streaming_info[i].max_sample_rate =
                usbAudioClass20_GetMaxSamplingRate(&intf_config->clock_sources[0]);

        DEBUG_LOG_WARN("uac2: max_sample_rate %d", streaming_info[i].max_sample_rate);

        if (!streaming_info[i].max_sample_rate)
        {
            DEBUG_LOG_ERROR("uac2: interface %d does not have CS config with unit_id %d",
                    i, streaming_info[i].clock_source);
            Panic();
        }

        /* set default sampling Rate */
        streaming_info[i].current_sampling_rate = streaming_info[i].max_sample_rate;

        PanicZero(streaming_info[i].current_sampling_rate);
    }
}

/****************************************************************************
    To add class 2.0 audio interface.
*/
static uac_ctx_t usbAudioClass20_Create(usb_device_index_t device_index,
                                     const usb_audio_config_params_t *config,
                                     usb_audio_streaming_info_t **streaming_info,
                                     uac_event_handler_t evt_handler)
{
    DEBUG_LOG_INFO("usbAudioClass20_Create");
    PanicZero(config);
    PanicNull(streaming_info);
    PanicZero(evt_handler);

    const usb_audio_interface_config_list_t *intf_list = config->intf_list;

    uac_data_t *uac_info = usbAudioClass20_GetNewInstance();

    uac_info->config = config;
    uac_info->num_interfaces = config->intf_list->num_interfaces;
    uac_info->evt_handler = evt_handler;

    uac_info->streaming_info = (usb_audio_streaming_info_t *)
            PanicUnlessMalloc(sizeof(usb_audio_streaming_info_t) *
                              uac_info->num_interfaces);
    memset(uac_info->streaming_info, 0,
           sizeof(usb_audio_streaming_info_t) * uac_info->num_interfaces);

    *streaming_info = uac_info->streaming_info;

    /* Attempt to register control interface */
    usbAudioClass20_EnumerateControl(uac_info);

    /* Set default volume and frequency */
    usbAudioClass20_Set_Defaults(uac_info);

    /* Attempt to register all interface */
    for (uint8 i=0; i < uac_info->num_interfaces; i++)
    {
        usbAudioClass20_EnumerateStreaming(device_index,
                                           &uac_info->streaming_info[i],
                                           &intf_list->intf[i]);
    }

    UsbCodes codes;
    codes.bInterfaceClass = USB_CLASS_AUDIO;
    codes.bInterfaceSubClass = UAC_IF_SUBCLASS_UNDEFINED;
    codes.bInterfaceProtocol = UAC_FN_PROTOCOL_VERSION_02_00;
    codes.iInterface = 0;

    uac_info->usb_iad_descriptor[0] = USB_DT_INTERFACE_ASSOCIATION_SIZE;      /* bLength */
    uac_info->usb_iad_descriptor[1] = USB_DT_INTERFACE_ASSOCIATION; /* bDescriptorType = INTERFACE_ASSOCIATION */
    uac_info->usb_iad_descriptor[2] = (uint8)uac_info->control_interface; /* bFirstInterface */
    uac_info->usb_iad_descriptor[3] = 0x03;      /* bInterfaceCount */
    uac_info->usb_iad_descriptor[4] = codes.bInterfaceClass; /* bFunctionClass */
    uac_info->usb_iad_descriptor[5] = codes.bInterfaceSubClass; /* bFunctionSubClass */
    uac_info->usb_iad_descriptor[6] = codes.bInterfaceProtocol; /* bFunctionProtocol */
    uac_info->usb_iad_descriptor[7] = codes.iInterface; /* iFunction */

    bool result = UsbAddInterfaceAssociationDescriptor(uac_info->control_interface,
                                                       uac_info->usb_iad_descriptor,
                                                       USB_DT_INTERFACE_ASSOCIATION_SIZE);
    assert(result);

    /* "USB Interface Association Descriptor Device Class Code and Use Model"
     * states that devices that use the IAD must use the device class, subclass
     * and protocol codes defined as Multi-Interface Function Device Class Codes */

#ifndef USB_CLASS_MISC
#define USB_CLASS_MISC 0xef
#endif

#ifndef USB_MISC_SUB_CLASS_COMMON
#define USB_MISC_SUB_CLASS_COMMON 2
#endif

#ifndef USB_MISC_COMMON_IAD
#define USB_MISC_COMMON_IAD 1
#endif

    uint32 class_codes =
            USB_CLASS_MISC |                   /* bDeviceClass - Multi-interface Function Code Device */
           (USB_MISC_SUB_CLASS_COMMON << 8) |  /* bDeviceSubClass - Common Class Sub Class */
           (USB_MISC_COMMON_IAD << 16);        /* bDeviceProtocol - Interface Association Descriptor protocol */
    if (!UsbHubConfigKey(USB_DEVICE_CFG_DEVICE_CLASS_CODES, class_codes))
    {
        Panic();
    }

    usbAudioClass20_ManageStreamConnection(uac_info);

    DEBUG_LOG_INFO("usbAudioClass20_Create success");
    return (uac_ctx_t) uac_info;
}

/****************************************************************************
    To delete usb audio class 2.0 instance which created using usbAudioClass20_Create
*/
static bool usbAudioClass20_Delete(uac_ctx_t class_ctx)
{
    uac_data_t **uac_info_ptr = &audio_class_info;

    while (*uac_info_ptr)
    {
        if (*uac_info_ptr == (uac_data_t *)class_ctx)
        {
            uac_data_t *uac_info = *uac_info_ptr;

            *uac_info_ptr = (*uac_info_ptr)->next;

            free(uac_info->cs_header_desc);
            free(uac_info->streaming_info);
            free(uac_info);

            return TRUE;
        }

        uac_info_ptr = &((*uac_info_ptr)->next);
    }

    DEBUG_LOG_ERROR("UAC2: delete failed");

    return FALSE;
}

static bool usbAudioClass20_Reset(uac_ctx_t class_ctx)
{
    uac_data_t *uac_info = audio_class_info;

    while (uac_info)
    {
        if (uac_info == (uac_data_t *)class_ctx)
        {
            usbAudioClass20_Set_Defaults(uac_info);
            return TRUE;
        }

        uac_info = uac_info->next;
    }

    DEBUG_LOG_ERROR("UAC2: reset failed");

    return FALSE;
}

static usb_interface_result_t usbAudioClass20_SetAudioInterface(uac_ctx_t class_ctx,
        uint16 interface, uint16 alt_setting)
{
    uac_data_t *uac_info = audio_class_info;

    DEBUG_LOG_INFO("UAC2: SetInterface %d alt %d", interface, alt_setting);

    while (uac_info)
    {
        if (uac_info == (uac_data_t *)class_ctx)
        {
            const usb_audio_interface_config_list_t *intf_list = uac_info->config->intf_list;

            for (uint8 index=0; index < intf_list->num_interfaces; index++)
            {
                if (uac_info->streaming_info[index].interface == interface)
                {
                    const usb_audio_interface_config_t *intf_config = &intf_list->intf[index];
                    usb_audio_streaming_info_t *intf_info = &uac_info->streaming_info[index];

                    DEBUG_LOG_INFO("UAC2: SetAudioInterface %d alt %d ", interface, alt_setting);

                    intf_info->alt_setting = alt_setting;

                    if(alt_setting == USB_ALT_SETTING_ZERO ||
                            alt_setting == USB_ALT_SETTING_NONE)
                    {
                        // clear interface
                    }
                    else /* update streaming interface config */
                    {
                        uint8 intf_index = INTF_DESC_INDEX_FROM_ALT_SETTINGS(alt_setting);
                        const uac_streaming_config_t *streaming_config =
                                &(intf_config->streaming_config[intf_index]);

                        PanicFalse(intf_index < intf_config->alt_settings_count);

                        const uac2_format_descriptor_t *format_desc =
                                usbAudioClass20_GetStreamingFormatDescriptor(streaming_config);
                        assert(format_desc);
                        intf_info->frame_size = format_desc->bSubslotSize;
                        intf_info->streaming_desc_index = intf_index;

                        const uac2_as_general_descriptor_t *as_general_desc =
                                usbAudioClass20_GetStreamingGeneralDescriptor(streaming_config);
                        assert(as_general_desc);

                        intf_info->channels = as_general_desc->bNrChannels;

                        intf_info->binterval = streaming_config->endpoint->bInterval;

                        intf_info->mps = usbAudioClass20_GetMaxPacketSize(streaming_config,
                                intf_info->max_sample_rate);
                    }

                    /* notify change in alternate interface */
                    uac_info->evt_handler(uac_info, index, USB_AUDIO_CLASS_MSG_ALT_INTF);

                    /* UsbDevice_CompleteAltInterfaceRequest() must be called when
                     * Audio subsystem is ready to serve/release USB endpoints */
                    return intf_info->chain_change_pending ?
                            USB_INTERFACE_PENDING :
                            USB_INTERFACE_READY;
                }
            }
            break;
        }

        uac_info = uac_info->next;
    }
    return USB_INTERFACE_NOT_HANDLED;
}

static usb_fn_tbl_uac_if uac20_fn_tbl = {
    .Create = usbAudioClass20_Create,
    .SetAudioInterface = usbAudioClass20_SetAudioInterface,
    .Reset = usbAudioClass20_Reset,
    .Delete = usbAudioClass20_Delete,
};

usb_fn_tbl_uac_if *UsbAudioClass20_GetFnTbl(void)
{
    return &uac20_fn_tbl;
}
